home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ADA Programming Guide
/
ADA Programming Guide.iso
/
ada_gwu
/
script.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-01-30
|
9KB
|
370 lines
/*
* Script for MSDOS
* Version 1.1
* Written Nov 1987 by graham@sce.carleton.ca (Doug Graham)
*
* This program is similar to the UNIX command of the same name.
* When running it, output that appears on the console, will also
* be saved into a file for later perusal.
*
* usage is: "script [-f outputfile] [-a] [command]"
*
* default outputfile is "typescript" in the current directory.
*
* -a means to append to outfile.
*
* Script optionally takes a command as argument. If one is given,
* this command is executed rather than "command.com".
* This saves having an extra copy of command.com wasting space
* in memory.
*
* BUGS:
*
* 1) On output, script first writes all the data to the output
* file, and then chains to DOS so that DOS does the actual console
* output. If a ^C is hit while this console output is taking place,
* console output is stopped. However this data has already been
* written to the output file. The result is that more data can appear
* in the output file than actually appeared on the screen.
*
* 2) On input, script calls DOS using it's own stack and with
* the flag "onintstack" set. (See int21.asm) If a ^C is typed
* in response to the input request, the calling program is
* aborted leaving "onintstack" set. This causes script to stop
* saving output to the output file.
*/
/*
* Modification History:
* Sept 2/89 Doug Graham.
* Modified to work with Turbo C 2.0 compiler.
* Also fiddled it a bit to reduce size.
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <process.h>
#include <dos.h>
#include <time.h>
static int fd;
static int mypsp;
static int criterr_occurred;
static char far *dosflag;
extern char far *getdosflag();
/*
* It might be that heaplen should be increased if
* there is a lot of stuff in the environment, because the TC startup
* routines mallocate space for both the environment, and the command
* line arguments.
*/
unsigned _heaplen = 1000;
unsigned _stklen = 1000;
main(argc, argv)
char **argv;
{
int oflags = O_WRONLY|O_CREAT|O_TRUNC|O_TEXT;
char *ofile = "typescript";
char *command;
char *GetTime();
for (--argc,++argv; argc && (argv[0][0] == '-'); --argc,++argv) {
switch(argv[0][1]) {
case 'f':
if (! --argc)
usage();
ofile = *++argv;
break;
case 'a':
oflags = (oflags & ~O_TRUNC) | O_APPEND;
break;
default:
usage();
}
}
command = argc ? *argv : getenv("COMSPEC");
if (! command)
command = "command";
if ((fd = open(ofile, oflags, S_IREAD|S_IWRITE)) < 0) {
mprintf("Can't open %s for writing\r\n", ofile);
exit(1);
}
dosflag = getdosflag();
mypsp = getpsp();
if (! grab21()) {
mprintf("I think script is already active\r\n");
exit(1);
}
mprintf("Script V1.1 session started %s\r\n", GetTime());
if (spawnvp(P_WAIT, command, argv) == -1)
mprintf("Can't execute %s\r\n", command);
mprintf("Script completed %s\r\n", GetTime());
rstr21();
flushbuf();
close(fd);
mprintf("Output file is %s\r\n", ofile);
exit(0);
}
usage()
{
mprintf("usage: script [-f outputfile] [-a] [command]\r\n");
exit(1);
}
#define BUFFERSIZE 4096
static char buffer[BUFFERSIZE];
static int bufslots = BUFFERSIZE;
static char *bufp = buffer;
#define _putc(c) \
{*bufp++ = c; if (! --bufslots) flushbuf();}
/*
* When flushing the buffer to disk, we use the undocumented DOS function
* 50h (set PSP) so that script's file handles are used rather
* than the the calling program's. I'm not sure in which versions
* of DOS this function exists.
*
* Control break checking is turned off in case the user types a
* ^C just as we are about to do the write to disk. Since we switched
* PSP's, DOS thinks we are the active program, and if ^C is typed
* with break checking enabled, it is us that will be aborted rather
* than the program running under us. This causes major havoc.
* The DOS critical error vector is intercepted for the same reason.
* Our handler simply sets a flag if an error occurs; this flag is
* checked at a later time.
*/
struct tcframe {int bp, di, si, ds, es, dx, cx, bx; unsigned char al, ah;};
void interrupt
my_criterr_handler(regs)
struct tcframe regs;
{
criterr_occurred = 1;
regs.al = 0; /* Zero means ignore the error. */
}
#define CRITERR_VECT 0x24
flushbuf()
{
int hispsp;
int hiscbrk;
void interrupt (* old_criterr_handler)();
hiscbrk = getcbrk();
setcbrk(0);
old_criterr_handler = getvect(CRITERR_VECT);
setvect(CRITERR_VECT, my_criterr_handler);
hispsp = getpsp();
setpsp(mypsp);
criterr_occurred = 0;
_write(fd, buffer, bufp - buffer);
bufslots = BUFFERSIZE;
bufp = buffer;
setpsp(hispsp);
setvect(CRITERR_VECT, old_criterr_handler);
setcbrk(hiscbrk);
if (criterr_occurred)
mprintf("\r\n\r\nSCRIPT: WARNING: disk write failed\r\n\r\n");
}
#define isconsole(handle) ((ioctl(handle, 0) & 0x82) == 0x82)
union MYFRAME {
struct {unsigned int ax, bx, cx, dx, ds, es;} x;
struct {unsigned char al, ah, bl, bh, cl, ch, dl, dh;} h;
};
/*
* DOS output functions. There are probably more, but these seem
* to do the job for me.
*/
#define CHAR_OUT 0x02
#define DIRECT_OUT 0x06
#define STRING_OUT 0x09
#define WRITE_FILE 0x40
/*
* DOS input functions. The input functions which also echo the
* input to the console must be intercepted, because otherwise
* this echo output would not be saved in the script file. There
* are probably more of these type of functions, but my documentation
* is not clear on which functions echo, and which do not, and I
* don't have the patience to go through and try each one.
*/
#define CHAR_IN_ECHO 0x01
#define READ_FILE 0x3F
#define BUFFERED_INPUT 0x0A
/*
* Returning the ZERO_FLAG to the first level handler tells
* it to chain to the old int 21 handler. If this bit is
* not set in the returned value, no such chaining occurs.
*/
#define ZERO_FLAG 0x40
#define CARRY_FLAG 0x01
/*
* Called by the assembly language first level handler. The first level handler
* first switches stacks, builds a stack frame that looks "union MYFRAME"
* and then calls "int21handler".
*/
unsigned
int21handler(regs)
union MYFRAME regs;
{
unsigned char far *fp;
int c, len, flags;
/*
* A bit of paranoia below. Since this function is only called
* when a program is trying to call DOS, it could possibly be
* safely assumed that it is not already in DOS. Just to be
* sure, I check the undocumented "indos" flag. It is important
* that nobody is in DOS when this procedure executes because
* it calls DOS itself, and DOS is not re-entrant.
*/
if (*dosflag)
return (ZERO_FLAG);
switch (regs.h.ah) {
case CHAR_OUT:
if (isconsole(1))
_putc(regs.h.dl);
break;
case DIRECT_OUT: /* This ones a real crock!! */
if ((isconsole(1)) && (regs.h.dl != 0xFF))
_putc(regs.h.dl);
break;
case STRING_OUT:
if (isconsole(1)) {
fp = (unsigned char far *)MK_FP(regs.x.ds, regs.x.dx);
while ((c = *fp++) != '$')
_putc(c);
}
break;
case WRITE_FILE:
if (isconsole(regs.x.bx)) {
fp = (unsigned char far *)MK_FP(regs.x.ds, regs.x.dx);
for (len = regs.x.cx; len--; )
_putc(*fp++);
}
break;
case READ_FILE:
if (isconsole(regs.x.bx)) {
fp = (unsigned char far *)MK_FP(regs.x.ds, regs.x.dx);
flags = callDOS(®s);
if (! (flags & CARRY_FLAG))
for (len = regs.x.ax; len--; )
_putc(*fp++);
return (flags & ~ZERO_FLAG);
}
break;
case CHAR_IN_ECHO:
if (isconsole(0)) {
flags = callDOS(®s);
_putc(regs.h.al);
return (flags & ~ZERO_FLAG);
}
break;
case BUFFERED_INPUT:
if (isconsole(0)) {
flags = callDOS(®s);
fp = (unsigned char far *)MK_FP(regs.x.ds, regs.x.dx);
for (len = *++fp; len--; )
_putc(*++fp);
return (flags & ~ZERO_FLAG);
}
break;
}
return (ZERO_FLAG);
}
/*
* Use my own printf in order to save a couple of Kbytes in the executable.
* The real one will work if necessary. This should be using varargs and
* vsprintf.
*/
mprintf(fmt, a1)
char *fmt, *a1;
{
char lbuf[128];
_write(1, lbuf, sprintf(lbuf, fmt, a1));
}
#if 0
char *GetTime()
{
long tyme;
time(&tyme);
return (ctime(&tyme));
}
#else
/*
* Use my own gettime, in order to save about 4K in the output file.
* Also had to write GetDate in assembler, because for some strange
* reason, the Turbo C libarary routine getdate does not appear to return
* the day of the week.
*/
struct Date {
int da_year;
char da_day;
char da_mon;
char da_weekday;
};
struct Time {
char ti_hund;
char ti_sec;
char ti_min;
char ti_hour;
};
char Days[] = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat\0???\0";
char Mons[] = "???\0Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec\0";
char *GetTime()
{
static char timebuf[32];
struct Date d;
struct Time t;
Getdate(&d);
Gettime(&t);
sprintf(timebuf, "%s %s %02d %02d:%02d:%02d %4d",
&Days[(d.da_weekday & 0x7) << 2],
&Mons[d.da_mon << 2],
d.da_day,
t.ti_hour,
t.ti_min,
t.ti_sec,
d.da_year);
return (timebuf);
}
#endif